/*=========================================================================

Program:   Visualization Toolkit
Module:    $RCSfile: WxvtkFreeTypeUtilitiesUnicode.cxx,v $

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

#include "WxvtkFreeTypeUtilitiesUnicode.h"

#include "vtkObjectFactory.h"
#include "vtkMath.h"
#include "vtkImageData.h"
#include "vtkTransform.h"

// FTGL

#include "ftgl/vtkftglConfig.h"
#include "ftgl/src/FTLibrary.h"
#include "ftgl/src/FTGLPixmapFont.h"

// The embedded fonts

#include "fonts/vtkEmbeddedFonts.h"

#include <sys/stat.h>

// Print debug info

#define VTK_FTFC_DEBUG 0
#define VTK_FTFC_DEBUG_CD 0

//----------------------------------------------------------------------------
vtkCxxRevisionMacro(WxvtkFreeTypeUtilitiesUnicode, "$Revision: 1.30 $");
vtkInstantiatorNewMacro(WxvtkFreeTypeUtilitiesUnicode);

//----------------------------------------------------------------------------
// The singleton, and the singleton cleanup

WxvtkFreeTypeUtilitiesUnicode* WxvtkFreeTypeUtilitiesUnicode::Instance = NULL;
WxvtkFreeTypeUtilitiesUnicodeCleanup WxvtkFreeTypeUtilitiesUnicode::Cleanup;

//----------------------------------------------------------------------------
// The embedded fonts
// Create a lookup table between the text mapper attributes 
// and the font buffers.

struct EmbeddedFontStruct
{
	size_t length;
	unsigned char *ptr;
};

//----------------------------------------------------------------------------
// This callback will be called by the FTGLibrary singleton cleanup destructor
// if it happens to be destroyed before our singleton (this order is not 
// deterministic). It will destroy our singleton, if needed.

void WxvtkFreeTypeUtilitiesUnicodeCleanupCallback ()
{
#if VTK_FTFC_DEBUG_CD
        printf("WxvtkFreeTypeUtilitiesUnicodeCleanupCallback\n");
#endif
        WxvtkFreeTypeUtilitiesUnicode::SetInstance(NULL);
}

//----------------------------------------------------------------------------
// Create the singleton cleanup
// Register our singleton cleanup callback against the FTLibrary so that
// it might be called before the FTLibrary singleton is destroyed.

WxvtkFreeTypeUtilitiesUnicodeCleanup::WxvtkFreeTypeUtilitiesUnicodeCleanup()
{
#if VTK_FTFC_DEBUG_CD
        printf("WxvtkFreeTypeUtilitiesUnicodeCleanup::WxvtkFreeTypeUtilitiesUnicodeCleanup\n");
#endif
        FTLibraryCleanup::AddDependency(&WxvtkFreeTypeUtilitiesUnicodeCleanupCallback);
}

//----------------------------------------------------------------------------
// Delete the singleton cleanup 
// The callback called here might have been called by the FTLibrary singleton
// cleanup first (depending on the destruction order), but in case ours is
// destroyed first, let's call it too.

WxvtkFreeTypeUtilitiesUnicodeCleanup::~WxvtkFreeTypeUtilitiesUnicodeCleanup()
{
#if VTK_FTFC_DEBUG_CD
        printf("WxvtkFreeTypeUtilitiesUnicodeCleanup::~WxvtkFreeTypeUtilitiesUnicodeCleanup\n");
#endif
        WxvtkFreeTypeUtilitiesUnicodeCleanupCallback();
}

//----------------------------------------------------------------------------
WxvtkFreeTypeUtilitiesUnicode* WxvtkFreeTypeUtilitiesUnicode::GetInstance()
{
        if (!WxvtkFreeTypeUtilitiesUnicode::Instance)
	{
                WxvtkFreeTypeUtilitiesUnicode::Instance = static_cast<WxvtkFreeTypeUtilitiesUnicode *>(
                        vtkObjectFactory::CreateInstance("WxvtkFreeTypeUtilitiesUnicode"));
                if (!WxvtkFreeTypeUtilitiesUnicode::Instance)
		{
                        WxvtkFreeTypeUtilitiesUnicode::Instance = new WxvtkFreeTypeUtilitiesUnicode;
		}
	}
        return WxvtkFreeTypeUtilitiesUnicode::Instance;
}

//----------------------------------------------------------------------------
void WxvtkFreeTypeUtilitiesUnicode::SetInstance(WxvtkFreeTypeUtilitiesUnicode* instance)
{
        if (WxvtkFreeTypeUtilitiesUnicode::Instance == instance)
	{
		return;
	}

        if (WxvtkFreeTypeUtilitiesUnicode::Instance)
	{
                WxvtkFreeTypeUtilitiesUnicode::Instance->Delete();
	}

        WxvtkFreeTypeUtilitiesUnicode::Instance = instance;

	// User will call ->Delete() after setting instance

	if (instance)
	{
		instance->Register(NULL);
	}
}

//----------------------------------------------------------------------------
WxvtkFreeTypeUtilitiesUnicode* WxvtkFreeTypeUtilitiesUnicode::New()
{
        WxvtkFreeTypeUtilitiesUnicode* ret = WxvtkFreeTypeUtilitiesUnicode::GetInstance();
	ret->Register(NULL);
	return ret;
}

//----------------------------------------------------------------------------
WxvtkFreeTypeUtilitiesUnicode::WxvtkFreeTypeUtilitiesUnicode()
{
#if VTK_FTFC_DEBUG_CD
        printf("WxvtkFreeTypeUtilitiesUnicode::WxvtkFreeTypeUtilitiesUnicode\n");
#endif

	this->NumberOfEntries = 0;
	this->InitializeCache();
	
}

//----------------------------------------------------------------------------
WxvtkFreeTypeUtilitiesUnicode::~WxvtkFreeTypeUtilitiesUnicode()
{
#if VTK_FTFC_DEBUG_CD
        printf("WxvtkFreeTypeUtilitiesUnicode::~WxvtkFreeTypeUtilitiesUnicode\n");
#endif

	this->ReleaseCache();
	
}


//----------------------------------------------------------------------------
void WxvtkFreeTypeUtilitiesUnicode::PrintSelf(ostream& os, vtkIndent indent)
{
	this->Superclass::PrintSelf(os,indent);
}

//----------------------------------------------------------------------------
// Print entry

void WxvtkFreeTypeUtilitiesUnicode::PrintEntry(int i, char *msg)
{
	if (!this->Entries[i])
	{
		return;
	}

	printf("%s: [%2d] =", msg, i);

        WxvtkTextPropertyUnicode *tprop = this->Entries[i]->TextProperty;
	if (tprop)
	{
		printf(" [S: %2d]", tprop->GetFontSize());

		double *color = tprop->GetColor();
		if (color)
		{
			printf(" [RGBA: %.2f/%.2f/%.2f (%.2f)]", 
				color[0], color[1], color[2], tprop->GetOpacity());
		}

		/*
                if (tprop->GetFontFileName())
		{
                printf(" [F: %s]", tprop->GetFontFileName());
		}
		else
		*/
		{
			printf(" [F: %d] [I: %d] [B: %d]", 
				tprop->GetFontFamily(), tprop->GetItalic(), tprop->GetBold());
		}
	}

	if (this->Entries[i]->Font)
	{
		printf(" [F: %p]", static_cast<void *>(this->Entries[i]->Font));
		printf("\n                                                [f: %p]", 
			static_cast<void*>(*(this->Entries[i]->Font->Face()->Face())));
	}

	printf("\n");
	fflush(stdout);
}

//----------------------------------------------------------------------------
// Release entry

void WxvtkFreeTypeUtilitiesUnicode::ReleaseEntry(int i)
{
	if (!this->Entries[i])
	{
		return;
	}

#if VTK_FTFC_DEBUG
	this->PrintEntry(this->NumberOfEntries, "Rl");
#endif

	if (this->Entries[i]->TextProperty)
	{
		this->Entries[i]->TextProperty->Delete();
		this->Entries[i]->TextProperty = NULL;
	}

	if (this->Entries[i]->Font)
	{
		delete this->Entries[i]->Font;
		this->Entries[i]->Font = NULL;
	}

	delete this->Entries[i];
	this->Entries[i] = NULL;
}

//----------------------------------------------------------------------------
// Initialize cache

void WxvtkFreeTypeUtilitiesUnicode::InitializeCache()
{
#if VTK_FTFC_DEBUG_CD
        printf("WxvtkFreeTypeUtilitiesUnicode::InitializeCache()\n");
#endif  
	this->ReleaseCache();

	int i;
	for (i = 0; i < VTK_FTFC_CACHE_CAPACITY; i++)
	{
		this->Entries[i] = NULL;
	}
}

//----------------------------------------------------------------------------
// Release cache

void WxvtkFreeTypeUtilitiesUnicode::ReleaseCache()
{
#if VTK_FTFC_DEBUG_CD
        printf("WxvtkFreeTypeUtilitiesUnicode::ReleaseCache()\n");
#endif  

	int i;
	for (i = 0; i < this->NumberOfEntries; i++)
	{
#if VTK_FTFC_DEBUG
		this->PrintEntry(i, "Rl");
#endif
		this->ReleaseEntry(i);
	}

	this->NumberOfEntries = 0;
}

//----------------------------------------------------------------------------
// Get a font from the cache given the text property. If no font is
// found in the cache, one is created and stored with the given color
// parameters. If override_color is true, then red, green, blue are used as
// text color instead of the colors found in the WxvtkTextPropertyUnicode.

WxvtkFreeTypeUtilitiesUnicode::Entry*
WxvtkFreeTypeUtilitiesUnicode::GetFont(WxvtkTextPropertyUnicode *tprop,	double override_color[3])
{
	int i,j;

	// Get the requested color and opacity
        WxvtkTextPropertyUnicode* tpropU=dynamic_cast<WxvtkTextPropertyUnicode*>(tprop);
        if (!tpropU) return 0;

	double tprop_color[3];
	for (i = 0; i < 3; i++)
	{
		tprop_color[i] = override_color ? override_color[i] : tprop->GetColor()[i];
		if (tprop_color[i] < 0.0)
		{
			tprop_color[i] = 0.0;
		}
	}

	float tprop_opacity = 
		(tprop->GetOpacity() < 0.0) ? 1.0 : tprop->GetOpacity();

	// Has the font been cached ?
	for (i = 0; i < this->NumberOfEntries; i++)
	{
                WxvtkTextPropertyUnicode *entry_tprop = dynamic_cast<WxvtkTextPropertyUnicode* >(this->Entries[i]->TextProperty);
		double *entry_tprop_color = entry_tprop->GetColor();

		if (
			// If a face file name has been specified, it overrides the 
			// font family as well as italic and bold attributes

                        (
                        entry_tprop->GetFontFileName() && tpropU->GetFontFileName() &&
                        !strcmp(entry_tprop->GetFontFileName(), tpropU->GetFontFileName())
                        )

			&& (entry_tprop_color[0]      == tprop_color[0] &&
			entry_tprop_color[1]      == tprop_color[1] &&
			entry_tprop_color[2]      == tprop_color[2] &&
			entry_tprop->GetOpacity() == tprop_opacity)

			&& entry_tprop->GetFontSize() == tprop->GetFontSize())
		{
			// Make this the most recently used
			if (i != 0)
			{
                                WxvtkFreeTypeUtilitiesUnicode::Entry *tmp = this->Entries[i];
				for (j = i - 1; j >= 0; j--)
				{
					this->Entries[j+1] = this->Entries[j];
				}
				this->Entries[0] = tmp;
			}

			return this->Entries[0];
		}
	}
        //vtkErrorWithObjectMacro(
        //        tprop,
        //        << "not Chached");
	// OK the font is not cached, try to create one

	FTFont *font = new FTGLPixmapFont;
	const char* fontFileName=tpropU->GetFontFileName();
	if (!font->Open(fontFileName,false))
	{
		vtkErrorWithObjectMacro(
			tprop,
                        << "!!!!!!!!!!!!Unable to create font !"
                        << fontFileName);
		delete font;
		return 0;
        }

	// Set face size

	font->FaceSize(tprop->GetFontSize());

	// We need to make room for a new font
	if (this->NumberOfEntries == VTK_FTFC_CACHE_CAPACITY)
	{
#if VTK_FTFC_DEBUG
		printf("Cache is full, deleting last!\n");
#endif
		this->NumberOfEntries--;
	}

	// Add the new font

	if (this->Entries[this->NumberOfEntries])
	{
		
		this->ReleaseEntry(this->NumberOfEntries);
	}
        this->Entries[this->NumberOfEntries] = new WxvtkFreeTypeUtilitiesUnicode::Entry;

        this->Entries[this->NumberOfEntries]->TextProperty = WxvtkTextPropertyUnicode::New();

        WxvtkTextPropertyUnicode *entry_tprop =
                dynamic_cast<WxvtkTextPropertyUnicode* > (this->Entries[this->NumberOfEntries]->TextProperty);
	// Set the other info

	entry_tprop->ShallowCopy(tprop);
	entry_tprop->SetOpacity(tprop_opacity);
	entry_tprop->SetColor(tprop_color);

	this->Entries[this->NumberOfEntries]->Font = font;

	this->Entries[this->NumberOfEntries]->LargestAscender  =
		this->Entries[this->NumberOfEntries]->LargestDescender = -1;


#if VTK_FTFC_DEBUG
	this->PrintEntry(this->NumberOfEntries, "Cr");
#endif

        WxvtkFreeTypeUtilitiesUnicode::Entry *tmp = this->Entries[this->NumberOfEntries];

	this->NumberOfEntries++;
	return tmp;
}
